import java.io.*;
import java.util.regex.*;
import java.util.*;
import java.awt.*;

class Map
{
	public static final int NUM_NODES = 199;

	private Node[] nodes = new Node[NUM_NODES];
    private Node[] positions = new Node[6];
	Agent[] agents = new Agent[6];

	public Map(Random random)
	{
		this("../data/map.txt", random);
	}

	public Map(String fileName, Random random)
	{
		System.out.print("Loading map...\t");
		// Initiate nodes
        // TODO: Make this pretty and not hardcoded
        Scanner sc;
        try{
            sc = new Scanner(new File("../data/positions.txt"));
        }
        catch(Exception e){
			System.err.println("Error: " + e.getMessage());
            sc = new Scanner(System.in); //Just to have a scanner
        }
        int x;
        int y;
		for (int i = 0; i < NUM_NODES; i++) {
            x = sc.nextInt();
            y = sc.nextInt();
			this.nodes[i] = new Node(i+1, x, y);
		}

		// Read and parse map file for connections.
		try{
			Node n1;
			Node n2;
			int type;

			Pattern letter = Pattern.compile("\\S");
			File file = new File(fileName);
			Scanner input = new Scanner(file);
			String tmp;
			while (input.hasNextInt()) {
				n1 = this.nodes[input.nextInt()-1];
				n2 = this.nodes[input.nextInt()-1];
				tmp = input.next(letter);
				switch(tmp.charAt(0)) {
					case 'T':
						type = Agent.TAXI;
						break;
					case 'B':
						type = Agent.BUS;
						break;
					case 'U':
						type = Agent.UNDERGROUND;
						break;
					case 'X':
						type = Agent.BLACK;
						break;
					default:
						type = 5;
				}

				n1.addConnection(n2, type);
				n2.addConnection(n1, type);
			}
			input.close();
		}catch (Exception e){
			System.err.println("Error: " + e.getMessage());
		}
		System.out.println("done");

		// Initiate Detectives and Mr X
		positions = getRandomPositions(random);
		for(int i=0; i<5; i++)
		{
			agents[i] = new Detective(positions[i], this);
		}
		agents[5] = new MrX(positions[5], this);
	}

	public Node getNode(int n)
	{
		return this.nodes[n-1];
	}

	/**
	 * Return an array of 6 unique and random nodes
	 */
	public Node[] getRandomPositions(Random random)
	{
		// Keeps track of taken positions in `positionSet'. Adds new nodes if
		// they are not taken.
		Node positions[] = new Node[6];
		TreeSet<Integer> positionSet = new TreeSet<Integer>();
		Integer r;
		for(int i=0; i<6; i++) {
			r = new Integer(random.nextInt(NUM_NODES));
			while(!positionSet.add(r))
				r = new Integer(random.nextInt(NUM_NODES));
			positions[i] = this.nodes[r];
		}
		return positions;
	}

	public Node[] getAgentPositions()
	{
		Node[] positions = new Node[6];
		for(int i=0; i<6; i++) {
			positions[i] = agents[i].getPosition();
		}
		return positions;
	}
    /**
     * Return the closest agent to a certain point on the map
     */
    public int getClosestAgent(Point place)
    {
        Node[] positions = getAgentPositions();
        int closestAgent = 0;
        double distance = 9001;
        for (int i=0; i<6; i++)
        {
            double distanceToAgent = place.distance(positions[i].getMapPosition());
            if (distanceToAgent < distance)
            {
                closestAgent = i;
                distance = distanceToAgent;
            }
        }
        return closestAgent;
    }

    public Node getClosestNode(Point clicked, Set<Node> nodes)
    {
        Node closestNode = null;
        double distance = 9001;
        for (Node node : nodes)
        {
            double distanceToNode = clicked.distance(node.getMapPosition());
            if (distanceToNode < distance)
            {
                closestNode = node;
                distance = distanceToNode;
            }
        }
        return closestNode;
    }
}
